package com.redstar.HappyRefresh.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.redstar.HappyRefresh.constant.RedisConstant;
import com.redstar.HappyRefresh.service.FeedService;
import com.redstar.HappyRefresh.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class FeedServiceImpl implements FeedService { // 关注流，拉模式
    @Autowired
    private RedisTemplate redisTemplate; // redis模板类
    @Autowired
    private ObjectMapper objectMapper; // JSON转换

    @Override
    public void initFollowFeed(Long userId, List<Long> followList) { // 初始化用户收件箱
        long maxTime = new Date().getTime(); // 当前时间
        Date limitDate = DateUtil.addDateDays(new Date(), -7); // 7天前的时间
        long minTime = limitDate.getTime();
        // 获取当前用户收件箱中最新的一条数据
        Set<ZSetOperations.TypedTuple<Object>> typedTupleSet = redisTemplate.opsForZSet().rangeWithScores(RedisConstant.IN_FOLLOW + userId, -1, -1);
        if (!ObjectUtils.isEmpty(typedTupleSet)) {
            minTime = typedTupleSet.iterator().next().getScore().longValue();
        } else {
            log.info("收件箱中的数据为null");
        }
        init(userId, followList, minTime, maxTime);
    }

    private void init(Long userId, List<Long> followList, long min, long max) { // 初始化用户收件箱
        String out = RedisConstant.OUT_FOLLOW; // 发件箱，out:follow:feed:
        String in = RedisConstant.IN_FOLLOW; // 收件箱，in:follow:feed:
        // 查看被关注的人的发件箱
        List<Set<DefaultTypedTuple>> outList = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Long followId : followList) {
                // 从zset中获取最多50个元素及其分数，元素不足50个也不会报错，min <= 分数 <= max，偏移量为0，降序排序
                // key为out:follow:feed:${followId}
                connection.zRevRangeByScoreWithScores((out + followId).getBytes(), min, max, 0, 50);
            }
            return null;
        });
        // 放入收件箱
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Set<DefaultTypedTuple> tuples : outList) {
                // 依次遍历每个博主发布的视频的id集合
                if (!ObjectUtils.isEmpty(tuples)) {
                    for (DefaultTypedTuple tuple : tuples) {
                        Object value = tuple.getValue();
                        // ids.add(Long.parseLong(value.toString()));
                        // key为in:follow:feed:${userId}，转换为byte数组
                        byte[] key = (in + userId).getBytes();
                        try {
                            // 将视频id放入收件箱
                            connection.zAdd(key, tuple.getScore(), objectMapper.writeValueAsBytes(value));
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e.getMessage());
                        }
                        // 设置有效期为5天
                        connection.expire(key, RedisConstant.HISTORY_TIME);
                    }
                }
            }
            return null;
        });
    }

    @Override
    public void pusOutBoxFeed(Long userId, Long id, long time) { // 将videoId推入发件箱，用户发件箱中的数据不会过期
        redisTemplate.opsForZSet().add(RedisConstant.OUT_FOLLOW + userId, time, id);
    }

    @Override
    public void deleteOutBoxFeed(Long userId, List<Long> fans, Long id) { // 删除当前用户的发件箱以及粉丝的收件箱中的指定视频
        String t = RedisConstant.IN_FOLLOW; // in:follow:feed:，收件箱
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Long fan : fans) {
                connection.zRem((t + fan).getBytes(), String.valueOf(id).getBytes());
            }
            // key为out:follow:feed:${userId}
            connection.zRem((RedisConstant.OUT_FOLLOW + userId).getBytes(), String.valueOf(id).getBytes());
            return null;
        });
    }

    @Override
    public void deleteInBoxFeed(Long userId, List<Long> ids) { // / 删除收件箱中的videoIds
        redisTemplate.opsForZSet().remove(RedisConstant.OUT_FOLLOW + userId, ids);
    }
}
